package discover

import (
	"common/config"
	"common/etcdcli"
	"common/logs"
	"context"
	"encoding/json"
	"time"

	clientv3 "go.etcd.io/etcd/client/v3"
)

// 一个租约
type Register struct {
	etcdClient *clientv3.Client
	DialTime   int // 超时时间
	// ttl        int // 租约过期时间
	leaseId clientv3.LeaseID
	info    Server //
	closeCh chan struct{}
}

func NewRegister() *Register {
	return &Register{
		DialTime: 3,
	}
}
func (r Register) Close() {
	r.closeCh <- struct{}{}
}

func (r Register) Register(config config.EtcdConf) error {
	info := &Server{
		Name:    config.Register.Name,
		Addr:    config.Register.Addr,
		Ttl:     config.Register.Ttl,
		Weight:  config.Register.Weight,
		Version: config.Register.Version,
	}
	var err error
	r.info = *info
	r.etcdClient, err = etcdcli.GetInstance(config)
	if err != nil {
		logs.Error("get etcdcli client instance err:%v", err)
		return nil
	}
	r.register()
	// 定时续约 // 2. 定时续约 watcher
	go r.watcher()
	return nil
}

func (r Register) register() error {
	// 1. 创建租约
	ctx, cancel := context.WithTimeout(context.Background(), time.Second*time.Duration(r.DialTime))
	defer cancel()
	var err error
	if err = r.createLease(ctx, r.info.Ttl); err != nil {
		return err
	}

	// 3. 绑定租约
	data, _ := json.Marshal(r.info)
	return r.bindLease(ctx, r.info.BuildRegisterKey(), string(data))

}

func (r *Register) bindLease(ctx context.Context, key, value string) error {
	_, err := r.etcdClient.Put(ctx, key, value, clientv3.WithLease(r.leaseId))
	if err != nil {
		logs.Error("bindLease failed err : %v", err)
		return err
	}
	logs.Info("register server success key=%s", key)
	return nil
}

func (r *Register) createLease(ctx context.Context, ttl int64) error {
	grant, err := r.etcdClient.Grant(ctx, ttl)
	if err != nil {
		return err
	}
	r.leaseId = grant.ID
	return nil
}

func (r *Register) watcher() {
	ticker := time.NewTicker(time.Duration(r.info.Ttl) * time.Second)
	for {
		select {
		case <-r.closeCh:
			if err := r.unregister(); err != nil {
				logs.Error("close and unregister failed,err :%v", err)
			}
			if _, err := r.etcdClient.Revoke(context.Background(), r.leaseId); err != nil {
				logs.Error("close Revoke lease failed,err:%v", err)
			}
		case <-ticker.C:
			if err := r.register(); err != nil {
				logs.Error("ticker register failed err:%v", err)
			}
		}
	}

}

func (r *Register) unregister() error {
	_, err := r.etcdClient.Delete(context.Background(), r.info.BuildRegisterKey())
	return err

}
